/*

 * Licensed to the Apache Software Foundation (ASF) under one

 * or more contributor license agreements.  See the NOTICE file

 * distributed with this work for additional information

 * regarding copyright ownership.  The ASF licenses this file

 * to you under the Apache License, Version 2.0 (the

 * "License"); you may not use this file except in compliance

 * with the License.  You may obtain a copy of the License at

 *

 *     http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

package com.bff.gaia.unified.sdk.io;



import com.bff.gaia.unified.sdk.io.fs.ResolveOptions;

import com.bff.gaia.unified.sdk.io.fs.ResolveOptions.StandardResolveOptions;

import com.bff.gaia.unified.sdk.io.fs.ResourceId;

import org.apache.commons.lang3.SystemUtils;



import javax.annotation.Nullable;

import java.io.File;

import java.nio.file.Path;

import java.nio.file.Paths;

import java.util.Objects;

import java.util.UUID;



import static com.bff.gaia.unified.vendor.guava.com.google.common.base.Preconditions.*;



/** {@link ResourceId} implementation for local files. */

class LocalResourceId implements ResourceId {



  private final String pathString;



  @Nullable

  private transient volatile Path cachedPath;



  private final boolean isDirectory;



  static LocalResourceId fromPath(Path path, boolean isDirectory) {

    checkNotNull(path, "path");

    return new LocalResourceId(path, isDirectory);

  }



  private LocalResourceId(Path path, boolean isDirectory) {

    this.pathString =

        path.toAbsolutePath().normalize().toString() + (isDirectory ? File.separatorChar : "");

    this.isDirectory = isDirectory;

  }



  @Override

  public LocalResourceId resolve(String other, ResolveOptions resolveOptions) {

    checkState(isDirectory, "Expected the path is a directory, but had [%s].", pathString);

    checkArgument(

        resolveOptions.equals(StandardResolveOptions.RESOLVE_FILE)

            || resolveOptions.equals(StandardResolveOptions.RESOLVE_DIRECTORY),

        "ResolveOptions: [%s] is not supported.",

        resolveOptions);

    checkArgument(

        !(resolveOptions.equals(StandardResolveOptions.RESOLVE_FILE) && other.endsWith("/")),

        "The resolved file: [%s] should not end with '/'.",

        other);

    if (SystemUtils.IS_OS_WINDOWS) {

      return resolveLocalPathWindowsOS(other, resolveOptions);

    } else {

      return resolveLocalPath(other, resolveOptions);

    }

  }



  @Override

  public LocalResourceId getCurrentDirectory() {

    if (isDirectory) {

      return this;

    } else {

      Path path = getPath();

      Path parent = path.getParent();

      if (parent == null && path.getNameCount() == 1) {

        parent = Paths.get(".");

      }

      checkState(parent != null, "Failed to get the current directory for path: [%s].", pathString);

      return fromPath(parent, true /* isDirectory */);

    }

  }



  @Override

  @Nullable

  public String getFilename() {

    Path fileName = getPath().getFileName();

    return fileName == null ? null : fileName.toString();

  }



  private LocalResourceId resolveLocalPath(String other, ResolveOptions resolveOptions) {

    return new LocalResourceId(

        getPath().resolve(other), resolveOptions.equals(StandardResolveOptions.RESOLVE_DIRECTORY));

  }



  private LocalResourceId resolveLocalPathWindowsOS(String other, ResolveOptions resolveOptions) {

    String uuid = UUID.randomUUID().toString();

    Path pathAsterisksReplaced = Paths.get(pathString.replaceAll("\\*", uuid));

    String otherAsterisksReplaced = other.replaceAll("\\*", uuid);



    return new LocalResourceId(

        Paths.get(

            pathAsterisksReplaced

                .resolve(otherAsterisksReplaced)

                .toString()

                .replaceAll(uuid, "\\*")),

        resolveOptions.equals(StandardResolveOptions.RESOLVE_DIRECTORY));

  }



  @Override

  public String getScheme() {

    return "file";

  }



  @Override

  public boolean isDirectory() {

    return isDirectory;

  }



  Path getPath() {

    if (cachedPath == null) {

      cachedPath = Paths.get(pathString);

    }

    return cachedPath;

  }



  @Override

  public String toString() {

    return pathString;

  }



  @Override

  public boolean equals(Object obj) {

    if (!(obj instanceof LocalResourceId)) {

      return false;

    }

    LocalResourceId other = (LocalResourceId) obj;

    return this.pathString.equals(other.pathString) && this.isDirectory == other.isDirectory;

  }



  @Override

  public int hashCode() {

    return Objects.hash(pathString, isDirectory);

  }

}